package services

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/uber/tchannel-go/thrift"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/request"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/app/ops/zclients"
	data2 "go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/plat_limos_rpc"
	"go.didapinche.com/goapi/user_department_service_api"
	time2 "go.didapinche.com/time"
	"go.uber.org/zap"
	"sort"
	"strconv"
	"strings"
	"time"
)

type AppService interface {
	Find(ctx context.Context, query *request.SlaQuery) ([]map[string]interface{}, int, error)
	StatByGroup(ctx context.Context, query *request.SlaQuery) ([]*response.GroupSla, error)
	StatByAppIDS(ctx context.Context, query *request.AppSlaReq) (map[uint64]map[string]interface{}, error)
	ReloadApp(ctx context.Context, appID uint64) error
	//	GetGroup(ctx context.Context,groupID string)
}

type appService struct {
	repository         repositories.ApplicationRepository
	graphRepository    repositories.GraphRepository
	apiRepository      repositories.ApiRepository
	templateRepository repositories.TemplateRepository
	templateService    TemplateService
	department         user_department_service_api.TChanDepartmentService
	history            repositories.AlertHistoryRepository
	logger             *zap.Logger
	limos              plat_limos_rpc.TChanLimosService
}

func NewAppService(
	repository repositories.ApplicationRepository,
	history repositories.AlertHistoryRepository,
	limos plat_limos_rpc.TChanLimosService,
	templateRepository repositories.TemplateRepository,
	graphRepository repositories.GraphRepository,
	templateService TemplateService,
	apiRepository repositories.ApiRepository,
	department user_department_service_api.TChanDepartmentService,
	logger *zap.Logger,
) AppService {
	return &appService{
		limos:              limos,
		history:            history,
		department:         department,
		apiRepository:      apiRepository,
		templateService:    templateService,
		graphRepository:    graphRepository,
		templateRepository: templateRepository,
		logger:             logger.With(zap.String("type", "services.appService")),
		repository:         repository,
	}
}

// 查询app
func (s appService) Find(ctx context.Context, query *request.SlaQuery) ([]map[string]interface{}, int, error) {

	start, err := time.ParseInLocation("2006-01-02 15:04:05", query.Start, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, 0, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", query.End, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, 0, err
	}
	query.StartDate = data2.FormatDate(start, data2.Format_yyyyMMddHH)
	query.EndDate = data2.FormatDate(end, data2.Format_yyyyMMddHH)

	base := float64(end.Sub(start).Milliseconds())
	if query.GroupID != "" {
		groupIDS, err := zclients.GetNodeIDs(ctx, s.department, query.GroupID)
		if err != nil {
			s.logger.Error("call appService getNodeIDs error")
			return nil, 0, err
		}
		query.GroupIDS = groupIDS
		s.logger.Debug(fmt.Sprint(query.GroupIDS))
	}
	// 查询app
	applications, err := s.repository.Find(ctx, query.AppName, query.Level, query.GroupIDS)
	if err != nil {
		s.logger.Error("call ApplicationRepository Find error", zap.Error(err))
		return nil, 0, err
	}
	appIDMap := make(map[uint64]map[uint64]*response.History)
	appIDMap2 := make(map[uint64]map[uint64]*response.History)
	// 查询被设定sla的remplateID
	templates, err := s.templateRepository.FindByIsSla(ctx)
	if err != nil {
		s.logger.Error("call templateRepository FindByIsSla error", zap.Error(err))
		return nil, 0, errors.Wrap(err, "call templateRepository FindByIsSla error")
	}
	ids := make([]uint64, 0)
	tagsMap := make(map[uint64]int)
	for _, h := range templates {
		tagsMap[h.ID] = 0
		ids = append(ids, h.ID)
	}
	//处理前端传入的id
	ids2 := make([]uint64, 0)
	if query.TemplateIDS != "" {
		arr := strings.Split(query.TemplateIDS, ",")
		for _, a := range arr {
			id, err := strconv.ParseUint(a, 10, 64)
			if err == nil {
				ids2 = append(ids2, id)
			}
		}
	}
	for _, id := range ids2 {
		tagsMap[id] = 0
	}
	histories2, err := s.history.FindByDateGroupApp(ctx, ids2, query.Start, query.End)
	if err != nil {
		s.logger.Error("call AlertHistoryRepository FindByDateGroupApp error", zap.Error(err))
		return nil, 0, err
	}
	// 将告警历史处理为map结构，appID对应具体告警历史
	for _, h := range histories2 {
		if _, ok := appIDMap2[h.AppID]; !ok {
			tags := make(map[uint64]*response.History)
			tags[h.ID] = h
			appIDMap2[h.AppID] = tags
		} else {
			appIDMap2[h.AppID][h.ID] = h
		}
	}
	// 相关，appID对应具体告警历史
	histories, err := s.history.FindByDateGroupApp(ctx, ids, query.Start, query.End)
	if err != nil {
		s.logger.Error("call AlertHistoryRepository FindByDateGroupApp error", zap.Error(err))
		return nil, 0, err
	}
	for _, h := range histories {
		if _, ok := appIDMap[h.AppID]; !ok {
			tags := make(map[uint64]*response.History)
			tags[h.ID] = h
			appIDMap[h.AppID] = tags
		} else {
			appIDMap[h.AppID][h.ID] = h
		}
	}
	count := len(applications)
	data := make([]map[string]interface{}, 0)
	for _, app := range applications {
		m := make(map[string]interface{})
		m["app_id"] = app.ID
		m["app_name"] = app.Name
		m["description"] = app.Description
		m["git_url"] = app.GitURL
		m["owner"] = app.GetOwners()
		users := make([]string, 0)
		userIDs := make([]string, 0)
		for _, user := range app.GetOwners() {
			users = append(users, user.UserName)
			userIDs = append(userIDs, user.UserID)
		}
		m["users"] = users
		m["language"] = app.Language
		m["level"] = app.Level
		m["dev_group_id"] = app.DevGroupID
		m["dev_group_name"] = app.DevGroupName
		m["sort"] = query.Sort
		m["order"] = query.Order
		if query.Type == 1 {
			if v, ok := appIDMap2[app.ID]; ok {
				for k := range tagsMap {
					if v1, ok := v[k]; ok {
						key := strconv.Itoa(int(k))
						m[key] = float64(v1.Duration)
					} else {
						key := strconv.Itoa(int(k))
						m[key] = float64(0)
					}

				}
			} else {
				for k := range tagsMap {
					key := strconv.Itoa(int(k))
					m[key] = float64(0)
				}
			}
		} else {
			if v, ok := appIDMap2[app.ID]; ok {
				for k := range tagsMap {
					if v1, ok := v[k]; ok {
						key := strconv.Itoa(int(k))
						m[key] = float64(v1.DurationCount)
					} else {
						key := strconv.Itoa(int(k))
						m[key] = float64(0)
					}
				}
			} else {
				for k := range tagsMap {
					key := strconv.Itoa(int(k))
					m[key] = float64(0)
				}
			}
		}
		// 计算sla
		m["sla"] = s.Compute(appIDMap[app.ID], base)
		if query.UID != "" {
			if data2.ContainString(userIDs, query.UID) {
				data = append(data, m)
				continue
			} else {
				continue
			}
		}
		if query.GroupID != "" {
			if data2.ContainString(query.GroupIDS, app.DevGroupID) {
				data = append(data, m)
				continue
			} else {
				continue
			}
		}
		data = append(data, m)
	}
	count = len(data)
	// 排序，根据map结构中的order进行排序
	sort.Sort(data2.HistorySlice(data))
	pageSize := query.PageSize * (query.PageNum)
	pageNum := (query.PageNum - 1) * query.PageSize
	if pageSize > len(data) {
		pageSize = len(data)
	}
	if len(data) > 0 && count > 0 {
		data = data[pageNum:pageSize]
	}
	for _, d := range data {
		sla := fmt.Sprint(d["sla"]) + "%"
		d["sla"] = sla
	}
	return data, count, nil
}

//计算sla
func (s appService) Compute(historys map[uint64]*response.History, base float64) float64 {
	if historys == nil || len(historys) == 0 {
		return 100
	}
	var sum float64
	for _, h := range historys {
		sum += float64(h.Duration)
	}
	if base-sum < 0 {
		return 0
	}
	value2 := (base - sum) / base
	value2 = value2 * 100
	value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", value2), 64)
	s.logger.Debug("计算sla" + fmt.Sprint(value))
	return value
}

func (s appService) StatByGroup(ctx context.Context, query *request.SlaQuery) ([]*response.GroupSla, error) {
	start, err := time.ParseInLocation("2006-01-02 15:04:05", query.Start, time.Local)
	if err != nil {
		return nil, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", query.End, time.Local)
	if err != nil {
		return nil, err
	}
	base := end.Sub(start).Milliseconds()
	query.StartDate = data2.FormatDate(start, data2.Format_yyyyMMddHH)
	query.EndDate = data2.FormatDate(end, data2.Format_yyyyMMddHH)
	templates, err := s.templateRepository.FindByIsSla(ctx)
	if err != nil {
		s.logger.Error("call templateRepository FindByIsSla error", zap.Error(err))
		return nil, errors.Wrap(err, "call templateRepository FindByIsSla error")
	}
	ids := make([]uint64, 0)
	for _, h := range templates {
		ids = append(ids, h.ID)
	}
	ids2 := make([]uint64, 0)

	if query.TemplateIDS != "" {
		arr := strings.Split(query.TemplateIDS, ",")
		for _, a := range arr {
			id, err := strconv.ParseUint(a, 10, 64)
			if err == nil {
				ids2 = append(ids2, id)
			}
		}
	}

	//slas, err := s.history.FindSlaByLevelGroup(ctx, query.Level, ids, time2.Time{Time: start}, time2.Time{Time: end})
	//if err != nil {
	//	s.logger.Error("call history FindSlaByLevelGroup error", zap.Error(err))
	//	return nil, errors.Wrap(err, "call history FindSlaByLevelGroup error")
	//}
	tags, err := s.history.FindSlaByGroup(ctx, ids2, time2.Time{Time: start}, time2.Time{Time: end})
	if err != nil {
		s.logger.Error("call history FindSlaByGroup error", zap.Error(err))
		return nil, errors.Wrap(err, "call history FindSlaByGroup error")
	}
	groups, err := zclients.GetNodeIDs(ctx, s.department, "group-2")
	if err != nil {
		s.logger.Error("call zclients GetNodeIDs error", zap.Error(err))
		return nil, errors.Wrap(err, "call zclients GetNodeIDs error")
	}
	ctx2, _ := context.WithTimeout(ctx, 5*time.Second)
	wrapContext := thrift.Wrap(ctx2)
	node, err := s.department.FindAllNodes(wrapContext)
	if err != nil {
		s.logger.Error("call userService.FindGroupsOfDevelopment error")
		return nil, err
	}
	//groups := []string{"group-2"}
	nodeMap := zclients.GetMapID(node)
	s.logger.Debug("node:" + fmt.Sprint(node))
	groupSlas := make([]*response.GroupSla, 0)
	for _, g := range groups {
		groupSla := new(response.GroupSla)
		node2 := zclients.GetMapByNode(node, g)
		s.logger.Debug(g + "name:" + fmt.Sprint(node2.Name) + ";id:" + fmt.Sprint(node2.ID))
		s.logger.Debug(g + ":" + fmt.Sprint(node2))
		groupSla.Name = node2.Name
		groupSla.ParentID = nodeMap[g]
		groupSla.Level = 5 - zclients.GetNodeLevel(node2, 0)
		groupSla.ID = g
		m2 := make(map[string]float64)
		groups2Map := zclients.GetNodeIDMap(node2)
		groups2 := make([]string, 0)
		for k, _ := range groups2Map {
			groups2 = append(groups2, k)
		}
		s.logger.Debug(g + "下面的:" + fmt.Sprint(groups2))
		if err != nil {
			s.logger.Error("call zclients GetNodeIDs error", zap.Error(err))
			return nil, errors.Wrap(err, "call zclients GetNodeIDs error")
		}
		//groups2 := []string{"group-2", "group-160"}
		for _, id := range ids {
			m2[strconv.Itoa(int(id))] = 0
		}

		//level := 0
		//for i, sla := range slas {
		//	if data2.ContainString(groups2, sla.ID) {
		//		m2["sla"] += Compute(sla.Sla, float64(base))
		//		level++
		//		if i == len(slas)-1 {
		//			m2["sla"] = m2["sla"] / float64(level)
		//			m2["sla"], _ = strconv.ParseFloat(fmt.Sprintf("%.2f", m2["sla"]), 64)
		//		}
		//	}
		//}
		if m2["sla"] == 0 {
			m2["sla"] = 100
		}
		for _, tag := range tags {
			if data2.ContainString(groups2, tag.ID) {
				if data2.ContainUint(ids, tag.Tag) {
					groupSla.SlaSum += tag.Sla
					m2["sla"] = Compute(groupSla.SlaSum, float64(base))
					m2["sla"], _ = strconv.ParseFloat(fmt.Sprintf("%.2f", m2["sla"]), 64)
				}
				if query.Type == 1 {
					m2[strconv.Itoa(int(tag.Tag))] += tag.Sla
				} else {
					m2[strconv.Itoa(int(tag.Tag))] += tag.Count
				}
			}
		}

		groupSla.Stats = m2
		if groupSla.ID == "group-2" {
			groupSla.ParentID = ""
		}
		groupSlas = append(groupSlas, groupSla)
	}
	sort.Sort(response.GroupSlaSlice2(groupSlas))
	sort.Sort(response.GroupSlaSlice(groupSlas))

	return groupSlas, nil

}

func (s appService) StatByAppIDS(ctx context.Context, query *request.AppSlaReq) (map[uint64]map[string]interface{}, error) {
	start, err := time.ParseInLocation("2006-01-02 15:04:05", query.Start, time.Local)
	if err != nil {
		return nil, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", query.End, time.Local)
	if err != nil {
		return nil, err
	}
	base := end.Sub(start).Milliseconds()
	templates, err := s.templateRepository.FindByIsSla(ctx)
	if err != nil {
		s.logger.Error("call templateRepository FindByIsSla error", zap.Error(err))
		return nil, errors.Wrap(err, "call templateRepository FindByIsSla error")
	}
	var appIDS []uint64
	err = json.Unmarshal([]byte(query.IDS), &appIDS)
	query.AppIDS = appIDS
	ids := make([]uint64, 0)
	for _, h := range templates {
		ids = append(ids, h.ID)
	}
	if query.TemplateIDS != "" {
		arr := strings.Split(query.TemplateIDS, ",")
		ids = ids[0:0]
		for _, a := range arr {
			id, err := strconv.ParseUint(a, 10, 64)
			if err == nil {
				ids = append(ids, id)
			}
		}
	}
	slas, err := s.history.FindByDateAndAppIDsGroupApp(ctx, query.AppIDS, ids, query.Start, query.End)
	if err != nil {
		s.logger.Error("call history FindSlaByLevelGroup error", zap.Error(err))
		return nil, errors.Wrap(err, "call history FindSlaByLevelGroup error")
	}
	tags, err := s.history.FindByDateGroupApp(ctx, ids, query.Start, query.End)
	if err != nil {
		s.logger.Error("call history FindSlaByGroup error", zap.Error(err))
		return nil, errors.Wrap(err, "call history FindSlaByGroup error")
	}
	slasMap := make(map[uint64]int64)
	tagsMap := make(map[uint64]*response.History)
	for _, s := range slas {
		slasMap[s.AppID] = s.Duration
	}
	for _, t := range tags {
		tagsMap[t.AppID] = t
	}
	data := make(map[uint64]map[string]interface{}, 0)
	for _, appID := range query.AppIDS {
		m := make(map[string]interface{})
		if v, ok := slasMap[appID]; ok {
			m["sla"] = fmt.Sprint(Compute(float64(v), float64(base))) + "%"
		} else {
			m["sla"] = "100%"
		}
		for _, id := range ids {
			m[strconv.Itoa(int(id))] = 0
			if query.Type == 1 {
				if v, ok := tagsMap[appID]; ok {
					m[strconv.Itoa(int(id))] = v.Duration
				}
			} else {
				if v, ok := tagsMap[appID]; ok {
					m[strconv.Itoa(int(id))] = v.DurationCount
				}
			}
		}
		data[appID] = m
	}

	return data, nil
}

func (s appService) ReloadApp(ctx context.Context, appID uint64) error {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
		}
	}()
	apps, err := zclients.FindApp(ctx, s.limos)
	if err != nil {
		return errors.Wrap(err, "call zclients FindAppByID error")
	}
	app := new(plat_limos_rpc.App)
	for i, a := range apps.Apps {
		if a.ID == int64(appID) {
			app = a
			break
		}
		if i == len(apps.Apps)-1 {
			return errors.New("don't find app")
		}
	}
	var owners []*models.ApplicationOwner
	for i, id := range app.OwnerIds {
		owners = append(owners, &models.ApplicationOwner{
			UserID:   id,
			UserName: app.Owner[i],
		})
	}
	application := zclients.ConvertApp(app, owners)
	if err := s.repository.Save(ctx, application); err != nil {
		return errors.Wrap(err, "call repository Save error")
	}
	nodes := s.templateService.GetNodeByGroup(ctx)
	graphs, err := s.graphRepository.FindByKindAndID(ctx, int(application.ID), 0)
	if err != nil {
		return errors.Wrap(err, "call graphRepository FindByKindAndID error")
	}
	for _, g := range graphs {
		template, err := s.templateRepository.FindByID(ctx, g.TemplateId)
		if err != nil {
			continue
		}
		templates := make([]*models.Template, 0)
		templates = append(templates, template)
		s.templateService.ReplaceExpressionByApp(ctx, application, templates)
		graph := s.templateService.ChangeTemplate(ctx, templates, uint64(app.ID), nodes)
		if len(graph) > 0 {
			graph[0].ID = g.ID
			if graph[0].Alert != nil && g.Alert != nil {
				graph[0].Alert.Threshold = g.Alert.Threshold
			}
			s.graphRepository.Save(ctx, graph[0])
		}
	}

	return nil

}
